﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace SSG.Tools
{
    /// <summary>
    ///   Used in combination with <see cref = "ObjectTools.DeepCopy{T}" />
    /// </summary>
    /// <seealso cref = "ObjectTools.DeepCopy{T}" />
    /// <seealso cref = "ObjectTools.RecursivelyPostDeepCopy" />
    public interface IDeepCopy : ICloneable
    {
        /// <summary>
        ///   Called after an invocation of <see cref = "ObjectTools.DeepCopy{T}" /> on the copied (target) object.
        /// </summary>
        /// <seealso cref = "ObjectTools.DeepCopy{T}" />
        /// <seealso cref = "ObjectTools.RecursivelyPostDeepCopy" />
        void PostDeepCopy();
    }

    /// <summary>
    ///   Dealing with objects, class hierarchies, etc.
    /// </summary>
    public static class ObjectTools
    {
        private static readonly Regex GenericRegEx = new Regex(@"\[\w+.*?,");
        private static readonly List<Type> DeepCopyChecked = new List<Type>();
        private static readonly Dictionary<Type, SortedDictionary<string, int>> PropertyMappings = new Dictionary<Type, SortedDictionary<string, int>>();
        private static readonly Dictionary<Type, SortedDictionary<string, int>> FieldMappings = new Dictionary<Type, SortedDictionary<string, int>>();
        private static readonly Regex PropertyPrefixRegEx = new Regex(@"^[A-Z]+");

        #region Get namespace, classname etc.
        /// <summary>
        ///   Get the classname only, no namespace.
        /// </summary>
        /// <param name = "obj"></param>
        /// <param name="levels"></param>
        /// <returns></returns>
        public static string GetNamespace(this Object obj, int levels = -1)
        {
            if (obj == null || levels == 0) return null;
            string ns = (obj is Type) ? (obj as Type).Namespace : obj.GetType().Namespace;
            if (ns == null || levels < 0) return ns;
            int dots = ns.CountChar('.');
            if (levels >= dots + 1) return ns;

            // I need to cut off elements
            string[] parts = ns.Split(new[] {'.'}).ResizeArray(levels);
            return parts.EnumerationToString(".");
        }

        /// <summary>
        ///   Get the class name.
        /// </summary>
        /// <param name="o"> </param>
        /// <returns> </returns>
        public static string GetClassname(this object o)
        {
            return o == null ? null : o.GetType().GetClassname();
        }

        /// <summary>
        ///   Get the classname only, without namespace.
        /// </summary>
        /// <param name="type"> </param>
        /// <returns> </returns>
        public static string GetClassname(this Type type)
        {
            return type == null ? null : type.FullName.GetClassname();
        }

        /// <summary>
        ///   Split off the classname from a canonical name such as <c>ns1.ns2.Classname</c>. With generics a <paramref
        ///    name="fullName" />
        ///   can look like <c>MyNamespace.Tools.Base.SerializableDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</c>.
        /// </summary>
        /// <param name="fullName"> </param>
        /// <returns> </returns>
        /// <seealso cref="GetGenericsClassnameAppendix" />
        public static string GetClassname(this string fullName)
        {
            // The simple form of a full classname: ns1.ns2.Classname
            // Fullname with generics: BTK.Tools.Base.SerializableDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
            if (String.IsNullOrEmpty(fullName)) return null;

            // cut of generics part
            string fn;
            string app = null;
            if (fullName.Contains('`')) {
                int tick = fullName.IndexOf('`');
                fn = fullName.Substring(0, tick);
                app = fullName.GetGenericsClassnameAppendix(false);
            }
            else
                fn = fullName;

            int dot = fn.LastIndexOf('.');
            fn = dot < 0 ? fn : fn.Substring(dot + 1);
            return (app == null) ? fn : fn + app;
        }

        /// <summary>
        ///   Resolve all class names of a fullname containing "generics" information.
        ///   Such a name can look like <c>BTK.Tools.Base.SerializableDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</c>.
        /// </summary>
        /// <param name="fullName"> </param>
        /// <param name="returnFullClassnames"> </param>
        /// <returns> </returns>
        //  EXTERNAL: http://derekslager.com/blog/posts/2007/09/a-better-dotnet-regular-expression-tester.ashx
        //  EXTERNAL: http://www.codeproject.com/KB/dotnet/regextutorial.aspx
        public static List<string> GetGenericsClassnames(this string fullName, bool returnFullClassnames)
        {
            if (String.IsNullOrEmpty(fullName) || !fullName.Contains('`')) return null;
            int tick = fullName.IndexOf('`');
            string generics = fullName.Substring(tick + 1);
            if (String.IsNullOrEmpty(generics)) return null;

            // find the classnames in the string
            // ReSharper disable ConditionIsAlwaysTrueOrFalse
            MatchCollection matches = GenericRegEx.Matches(generics);
            if (matches == null || matches.Count < 1) return null;
            // ReSharper restore ConditionIsAlwaysTrueOrFalse

            List<string> classes = new List<string>();
            // ReSharper disable LoopCanBeConvertedToQuery
            foreach (Match m in matches) {
                if (m == null) continue;
                string cn = m.Value;
                if (String.IsNullOrEmpty(cn)) continue;
                cn = cn.Replace('[', ' ').Replace(',', ' ').Trim();
                classes.Add(returnFullClassnames ? cn : cn.GetClassname());
            }
            // ReSharper restore LoopCanBeConvertedToQuery
            return classes;
        }

        /// <summary>
        ///   Create an appendix like <c>SerializableDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</c>,
        ///   but only provide the classnames (with or without namespaces depending on <paramref name="returnFullClassnames" />). This could return something like
        ///   <c>SerializableDictionary`2[[String],[String]]</c> and is used by <see cref="GetClassname(string)" />.
        /// </summary>
        /// <param name="fullName"> </param>
        /// <param name="returnFullClassnames"> </param>
        /// <returns> </returns>
        /// <seealso cref="GetClassname(string)" />
        public static string GetGenericsClassnameAppendix(this string fullName, bool returnFullClassnames)
        {
            if (String.IsNullOrEmpty(fullName)) return null;
            List<string> classes = fullName.GetGenericsClassnames(returnFullClassnames);
            if (classes.NullSafeIsEmpty()) return null;
            StringBuilder sb = new StringBuilder("`");
            sb.Append(classes.Count);
            sb.Append("[");
            string cs = classes.EnumerationToString(",", "[", "]");
            sb.Append(cs);
            sb.Append("]");
            return sb.ToString();
        }
        #endregion Get namespace, classname etc.

        #region Reflection support, getting properties ..
        /// <summary>
        ///   Get the name by using Lambda expression <c>()=> this.Name</c> as described in 
        ///   http://beta.blogs.microsoft.co.il/blogs/smallfish/archive/2009/04/02/how-to-get-a-property-name-using-lambda-expression-in-c-3-0.aspx
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="property"> </param>
        /// <returns> </returns>
        /// <example>
        ///   ObjectTools.GetPropertyName( () => tableObject.SQLTable )
        /// </example>
        public static string GetPropertyName<T>(Expression<Func<T>> property)
        {
            if (property == null) throw new ArgumentNullException("property");
            PropertyInfo propertyInfo = ((MemberExpression) property.Body).Member as PropertyInfo;

            if (propertyInfo == null) {
                throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
            }
            string propertyName = propertyInfo.Name;
            return propertyName;
        }

        /// <summary>
        ///   Helper for firing a property changed event, property expression 
        ///   is the same as in <see cref="GetPropertyName{T}" />.
        ///   <para> Important: Errors here (e.g. field not found) will be ignored during loading of settings. Also tracing is not ignored during this phase. </para>
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="targetObject"> </param>
        /// <param name="value"> new property value </param>
        /// <param name="valueChanged"> Did the value change, way to typesafely check the changed valued. </param>
        /// <param name="handler"> </param>
        /// <param name="property"> </param>
        /// <param name="setFieldByReflection"> automatically change the field by reflection, this does not always work, e.g. it fails with derived private fields </param>
        /// <returns> <c>true</c> if the value changed, i.e. <paramref name="valueChanged" /> is <c>true</c> </returns>
        /// <example>
        ///   this.SetValueAndFireOnPropertyChangeEvent(value, value == this._uiGridVisible, this.PropertyChanged, () => this.UiGridVisible)
        /// </example>
        /// <seealso cref="GetPropertyName{T}" />
        public static bool SetValueAndFireOnPropertyChangeEvent<T>(this object targetObject, object value, bool valueChanged, PropertyChangedEventHandler handler, Expression<Func<T>> property, bool setFieldByReflection)
        {
            // checks 
            if (!valueChanged) return false; // nothing to do
            if (targetObject == null) throw new ArgumentNullException("targetObject");
            if (property == null) throw new ArgumentNullException("property");
            if (!(targetObject is INotifyPropertyChanged))
                throw new ArgumentException(targetObject.GetType().FullName + " does not implement " + typeof (INotifyPropertyChanged).FullName);
            string propertyName = ObjectTools.GetPropertyName(property);
            if (String.IsNullOrEmpty(propertyName))
                throw new ArgumentException("Missing property name for expression " + property.Body);

            // set field
            try {
                // set new value
                if (setFieldByReflection) {
                    FieldInfo fi = targetObject.GetFieldInfoForPropertyName(propertyName, true);
                    // if this fails check naming convention on Property / Field
                    if (fi == null) throw new ArgumentException("No field found for property " + propertyName + " Naming convention met?");
                    fi.SetValue(targetObject, value);
                }

                // fire away
                if (handler == null) return true; // no subscribers
                handler(targetObject, new PropertyChangedEventArgs(propertyName));
            }
                // Log these errors:
                // Errors during loading of settings will be ignored
                // this logs the exception above (argument exception), but also any other
            catch (Exception e) {
                Trace.WriteLineIf(ToolsTraceSwitch.TraceError, "In 'SetValueAndFireOnPropertyChangeEvent', " + e.Message, targetObject.GetNamespace());
                throw; // re throw exception after logging
            }
            return true;
        }

        /// <summary>
        ///   Get the (public / private) field for a given <paramref name="propertyName" />.
        ///   <para> Searching is done by the "typical name conventions", e.g. MyProperty -> _myProperty. It fails when this convention is not used. </para>
        ///   <para> This is not able to find private properties of derived classes, use <see cref="GetPrivateFieldInClassHierarchy" /> for this purpose. </para>
        /// </summary>
        /// <param name="obj"> </param>
        /// <param name="propertyName"> </param>
        /// <param name="allowLenientCheck"> </param>
        /// <returns> </returns>
        /// <remarks>
        ///   Doomed to fail when obfuscation is used and the string for <paramref name="propertyName" /> is hardcoded.
        ///   Consider <see cref="GetPropertyName{T}" /> as an option.
        /// </remarks>
        /// <seealso cref="GetPrivateFieldInClassHierarchy" />
        /// <seealso cref="GetPropertyName{T}" />
        public static FieldInfo GetFieldInfoForPropertyName(this object obj, string propertyName, bool allowLenientCheck)
        {
            if (obj == null || String.IsNullOrEmpty(propertyName)) return null;

            // first check the typical name convention!
            string fieldName;
            string upperCasePrefix = PropertyPrefixRegEx.Match(propertyName).Value;
            int l = upperCasePrefix.NullSafeLength();
            if (l < 1)
                fieldName = "_" + propertyName; // myProperty -> _myProperty
            else if (l == 1)
                fieldName = "_" + upperCasePrefix.ToLower() + propertyName.Substring(1); // MyProperty -> _myProperty
            else
                fieldName = "_" + upperCasePrefix.Substring(0, l - 1).ToLower() + propertyName.Substring(l - 1); // SAPProperty _sapProperty

            FieldInfo fi = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (fi != null || !allowLenientCheck) return fi;

            // fallback, this most likely fails with obfuscation
            // but it solves all case sensitive issues.
            FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (FieldInfo fieldInfo in fields) {
                // ReSharper disable ConditionIsAlwaysTrueOrFalse
                if (fieldInfo == null || fieldInfo.Name == null) continue;
                // ReSharper restore ConditionIsAlwaysTrueOrFalse

                if (fieldInfo.Name.Equals(fieldName, StringComparison.CurrentCultureIgnoreCase) || fieldInfo.Name.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)) {
                    Trace.WriteLineIf(ToolsTraceSwitch.TraceWarning, "Field '" + fieldInfo.Name + "' found for property '" + propertyName + "', convention should be '" + fieldName, typeof (ObjectTools).Namespace + "'");
                    return fieldInfo;
                }
            }
            return null;
        }

        /// <summary>
        ///   Get a private instance field by walking up the class hierarchy
        /// </summary>
        /// <param name="objectType"> </param>
        /// <param name="fieldName"> </param>
        /// <returns> </returns>
        public static FieldInfo GetPrivateFieldInClassHierarchy(this Type objectType, string fieldName)
        {
            if (objectType == null || String.IsNullOrEmpty(fieldName)) return null;
            FieldInfo fieldInfo = objectType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            // Debugging: FieldInfo[] fieldInfos = objectType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);

            if (fieldInfo != null) return fieldInfo;
            return (objectType.BaseType != null) ? objectType.BaseType.GetPrivateFieldInClassHierarchy(fieldName) : null;
        }

        /// <summary>
        ///   Get all non public instance fields of the given type.
        /// </summary>
        /// <param name="t"> </param>
        /// <returns> </returns>
        public static List<FieldInfo> GetAllNonPublicInstanceFieldInfo(this Type t)
        {
            if (t == null) throw new ArgumentNullException("t");
            Contract.EndContractBlock();
            FieldInfo[] fi = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
            List<FieldInfo> fiList = fi.ToList();
            if (t.BaseType == null) return fiList;
            List<FieldInfo> subList = t.BaseType.GetAllNonPublicInstanceFieldInfo();
            if (subList.NullSafeIsEmpty()) return fiList;
            fiList.AddRangeWithoutDuplicates(subList);
            return fiList;
        }

        /// <summary>
        ///   Get the value for a static field of class <paramref name="classType" />.
        /// </summary>
        /// <param name="classType"> </param>
        /// <param name="fieldName"> </param>
        /// <returns> </returns>
        public static T GetStaticFieldValue<T>(this Type classType, string fieldName)
        {
            if (classType == null || String.IsNullOrEmpty(fieldName)) return default(T);
            FieldInfo fieldInfo = classType.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            if (fieldInfo == null) return default(T);
            return (T) fieldInfo.GetValue(classType);
        }

        /// <summary>
        ///   Field name to its index mapper.
        ///   Allows to quickly retrieving all field indexes by their names.
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="bf"> </param>
        /// <returns> </returns>
        public static SortedDictionary<string, int> GetFieldToIndexMapping<T>(BindingFlags bf)
        {
            return ObjectTools.GetFieldToIndexMapping(typeof (T), bf);
        }

        /// <summary>
        ///   Field name to its index mapper.
        ///   Allows to quickly retrieving all field indexes by their names.
        /// </summary>
        /// <param name="obj"> </param>
        /// <param name="bf"> </param>
        /// <returns> </returns>
        public static SortedDictionary<string, int> GetFieldToIndexMapping(this object obj, BindingFlags bf)
        {
            if (obj == null) throw new ArgumentNullException("obj");
            return ObjectTools.GetFieldToIndexMapping(obj.GetType(), bf);
        }

        /// <summary>
        ///   Get the field mappings (string to index) for a class (type).
        ///   Allows to quickly retrieving all field indexes by their names.
        /// </summary>
        /// <param name="t"> </param>
        /// <param name="bf"> </param>
        /// <returns> </returns>
        public static SortedDictionary<string, int> GetFieldToIndexMapping(Type t, BindingFlags bf)
        {
            if (t == null) throw new ArgumentNullException("t");
            SortedDictionary<string, int> mapping;
            if (ObjectTools.FieldMappings.TryGetValue(t, out mapping)) return mapping;
            mapping = new SortedDictionary<string, int>();
            FieldInfo[] fi = t.GetFields(bf);
            for (int i = 0; i < fi.Length; i++) {
                mapping.Add(fi[i].Name, i);
            }
            ObjectTools.FieldMappings.Add(t, mapping);
            return mapping;
        }

        /// <summary>
        ///   Get a property name to index mapping.
        ///   Allows to quickly retrieving all field indexes by their names.
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="bf"> </param>
        /// <returns> </returns>
        public static SortedDictionary<string, int> GetPropertyToIndexMapping<T>(BindingFlags bf)
        {
            return ObjectTools.GetPropertyToIndexMapping(typeof (T), bf);
        }

        /// <summary>
        ///   Get a property name to index mapping.
        ///   Allows to quickly retrieving all field indexes by their names.
        /// </summary>
        /// <param name="t"> </param>
        /// <param name="bf"> </param>
        /// <returns> </returns>
        public static SortedDictionary<string, int> GetPropertyToIndexMapping(Type t, BindingFlags bf)
        {
            if (t == null) throw new ArgumentNullException("t");
            PropertyInfo[] fi = t.GetProperties(bf);
            SortedDictionary<string, int> mapping;
            if (ObjectTools.PropertyMappings.TryGetValue(t, out mapping)) return mapping;
            mapping = new SortedDictionary<string, int>();
            for (int i = 0; i < fi.Length; i++) {
                mapping.Add(fi[i].Name, i);
            }
            ObjectTools.PropertyMappings.Add(t, mapping);
            return mapping;
        }

        /// <summary>
        ///   All non public fields which are serializable and those which are potentially
        ///   troublemakers <paramref name = "dangerousFieldInfo" />.
        /// </summary>
        /// <param name = "t"></param>
        /// <param name = "dangerousFieldInfo"></param>
        /// <returns></returns>
        public static List<FieldInfo> GetSerializableInstanceFieldInfo(this Type t, out List<FieldInfo> dangerousFieldInfo)
        {
            if (t == null) throw new ArgumentNullException("t");
            Contract.EndContractBlock();

            List<FieldInfo> fieldInfoList = GetAllNonPublicInstanceFieldInfo(t); // hierarchy resolved!
            fieldInfoList = fieldInfoList ?? new List<FieldInfo>();
            fieldInfoList.AddRangeWithoutDuplicates(t.GetFields(BindingFlags.Instance | BindingFlags.Public));
            dangerousFieldInfo = null;
            if (fieldInfoList.NullSafeIsEmpty()) return fieldInfoList;

            List<FieldInfo> serializableFieldInfo = new List<FieldInfo>();
            dangerousFieldInfo = new List<FieldInfo>();
            foreach (FieldInfo fieldInfo in fieldInfoList) {
                if (fieldInfo.IsNotSerialized) continue; // marked by attribute as [NonSerialized]
                Type fieldType = fieldInfo.FieldType;
                if (fieldType.IsSerializable) {
                    // IsSerializable is not a guarantee this can be "deep copied"
                    // standard framework types are not checked
                    if (fieldType.IsNetFrameworkSystemType() || ObjectTools.DeepCopyChecked.Contains(fieldType))
                        serializableFieldInfo.Add(fieldInfo);
                    else {
                        try {
                            // delegates should be marked with [field:NonSerialized]
                            // http://bytes.com/topic/net/answers/124265-c-serialization-events-problem
                            if (typeof (Delegate).IsSameOrSubclass(fieldType)) {
                                // since I get to this line, the field is not marked
                                // this makes the delegate a potential troublemaker
                                dangerousFieldInfo.Add(fieldInfo);
                                continue;
                            }

                            // Test the following:
                            // The System.Type of the object encompassed or referred to by the 
                            // - current array, pointer, or reference type, 
                            // or null if the current System.Type is
                            // - not an array or a pointer, or is not passed by reference, or represents a generic type or a 
                            //   type parameter in the definition of a generic type or generic method.
                            Type elementType = fieldType.GetElementType();
                            object instance;
                            if (elementType != null) {
                                // resolve arrays to underlying type, e.g. float[] -> float
                                instance = fieldType.IsArray ? Activator.CreateInstance(elementType) : Activator.CreateInstance(fieldType); // default constructor is required for serialization anyway
                            }
                            else {
                                // default constructor instance
                                instance = Activator.CreateInstance(fieldType);
                            }

                            // try a deep copy
                            object copy = instance.DeepCopy();
                            if (copy != null) {
                                ObjectTools.DeepCopyChecked.Add(fieldType);
                                serializableFieldInfo.Add(fieldInfo);
                            }
                            else {
                                dangerousFieldInfo.Add(fieldInfo);
                            }
                        }
                        catch {
                            dangerousFieldInfo.Add(fieldInfo);
                        }
                    }
                }
                else
                    dangerousFieldInfo.Add(fieldInfo); // non serializable type but not tagged as [NonSerializable]
            }
            return serializableFieldInfo;
        }
        #endregion Reflection support, getting properties ..

        #region Class hierarchy, type evaluation ...
        /// <summary>
        ///   Is this object an exception?
        /// </summary>
        /// <param name="o"> </param>
        /// <returns> </returns>
        public static bool IsException(object o)
        {
            if (o == null) return false;
            return (o.GetType().IsSubclassOf(typeof (Exception)) || o.GetType() == typeof (Exception));
        }

        /// <summary>
        ///   Is the <paramref name="possibleSubType" /> of the same class or is it 
        ///   a subclass of <paramref name="parentType" />.
        ///   <para> This is a (in details) different from the keyword <c>is</c> which checks, whether a cast is possible. </para>
        ///   <para> E.g., while <c>is string</c> is <c>true</c> for almost every parentType (because of <c>ToString()</c> ), this method relies on the class hierarchy and checks, whether <c>possibleSubType</c> is really a derived class. Also with interfaces there are differences. </para>
        /// </summary>
        /// <param name="parentType"> </param>
        /// <param name="possibleSubType"> </param>
        /// <returns> </returns>
        [Pure]
        public static bool IsSameOrSubclass(this Type parentType, Type possibleSubType)
        {
            if (parentType == null || possibleSubType == null) return false;
            return (parentType == possibleSubType || possibleSubType.IsSubclassOf(parentType));
        }

        /// <summary>
        ///   Get all subclasses of a certain class <paramref name="rootclass" />.
        /// </summary>
        /// <param name="rootclass"> </param>
        /// <param name="exclcudeAbstractClasses"> </param>
        /// <returns> </returns>
        public static List<Type> GetSubclasses(Type rootclass, bool exclcudeAbstractClasses)
        {
            if (rootclass == null) throw new ArgumentException("Rootclass must be provided");
            List<Type> subclasses = new List<Type>();
            return ObjectTools.GetSubclasses(rootclass, exclcudeAbstractClasses, subclasses);
        }

        /// <summary>
        ///   Get all subclasses of a certain class <paramref name="rootclass" />.
        ///   Only subclasses from the same <c>Assembly</c> will be found.
        /// </summary>
        /// <param name="rootclass"> </param>
        /// <param name="exclcudeAbstractClasses"> </param>
        /// <param name="subclasses"> </param>
        /// <returns> </returns>
        /// <seealso cref="Assembly" />
        private static List<Type> GetSubclasses(Type rootclass, bool exclcudeAbstractClasses, List<Type> subclasses)
        {
            if (rootclass == null || subclasses == null)
                throw new ArgumentException("List and rootclass must be provided");
            Assembly asm = rootclass.Assembly;

            // ReSharper disable LoopCanBeConvertedToQuery
            foreach (Type type in asm.GetTypes()) {
                if (!type.IsSubclassOf(rootclass)) continue;
                if (!type.IsAbstract || !exclcudeAbstractClasses) subclasses.Add(type);
            }
            // ReSharper restore LoopCanBeConvertedToQuery

            return subclasses;
        }

        /// <summary>
        ///   Find the common superclass, which eventually is <c>object</c>,
        /// </summary>
        /// <param name="types"> </param>
        /// <param name="requiredBaseClass"> Specify that all types must derive from this class (or <c>null</c> ) </param>
        /// <returns> </returns>
        public static Type FindCommonBaseClass(this List<Type> types, Type requiredBaseClass)
        {
            if (types.NullSafeIsEmpty()) return null;
            if (types.Count == 1) return types[0];

            Type commonBaseTypeCandiate = types[0];
            if (requiredBaseClass != null && !requiredBaseClass.IsSameOrSubclass(commonBaseTypeCandiate)) return null;

            while (commonBaseTypeCandiate != null) {
                bool failed = false;
                for (int i = 1; i < types.Count; i++) {
                    Type ct = types[i];
                    if (!commonBaseTypeCandiate.IsSameOrSubclass(ct)) {
                        failed = true;
                        break;
                    }
                }

                if (!failed) break;

                // one up
                commonBaseTypeCandiate = commonBaseTypeCandiate.BaseType;
                if (requiredBaseClass != null && !requiredBaseClass.IsSameOrSubclass(commonBaseTypeCandiate)) break;
            }

            // commonBaseTypeCandiate base type
            return commonBaseTypeCandiate;
        }

        /// <summary>
        ///   Get the inheritance distance for the given <paramref name="possibleSubType" />
        ///   to the <paramref name="parentClass" />, -1 if not a subclass. Zero (0) means
        ///   same types.
        /// </summary>
        /// <param name="parentClass"> </param>
        /// <param name="possibleSubType"> </param>
        /// <returns> </returns>
        public static int SubclassLevel(this Type parentClass, Type possibleSubType)
        {
            if (parentClass == possibleSubType) return 0;
            if (parentClass == null || possibleSubType == null) return -1;
            if (!possibleSubType.IsSubclassOf(parentClass)) return -1;

            int count = 1;
            Type parentType = possibleSubType.BaseType;
            do {
                if (parentType == null) throw new Exception("BaseType is null, but " + possibleSubType.FullName + " is subtype of " + parentClass.FullName); // this should never be the case
                if (parentType == parentClass) return count;
                count++;
                parentType = parentType.BaseType;
            }
            while (parentType != null || count > 100);

            // this should never be the case
            throw new Exception("BaseType is null, but " + possibleSubType.FullName + " is subtype of " + parentClass.FullName);
        }

        /// <summary>
        ///   Is the given <paramref name="type" /> a type of 
        ///   <c>System.</c> namespace of the .net framework.
        /// </summary>
        /// <param name="type"> </param>
        /// <returns> </returns>
        public static bool IsNetFrameworkSystemType(this Type type)
        {
            // ReSharper disable ConditionIsAlwaysTrueOrFalse
            if (type == null || type.FullName == null) return false;
            return type.FullName.StartsWith("System.");
            // ReSharper restore ConditionIsAlwaysTrueOrFalse
        }

        /// <summary>
        ///   Is the given <paramref name="type" /> a type of the .net framework.
        /// </summary>
        /// <param name="type"> </param>
        /// <returns> </returns>
        public static bool IsNetFrameworkType(this Type type)
        {
            if (type == null) return false;
            if (type.IsNetFrameworkSystemType()) return true;

            string title, description, company;
            type.Assembly.GetAssemblyMetadata(out title, out description, out company);
            return (!String.IsNullOrEmpty(company) && company.ToLower().Contains("microsoft"));
        }

        /// <summary>
        ///   Is <paramref name="type" /> a generic list <see cref="List{T}" />?
        /// </summary>
        /// <param name="type"> </param>
        /// <param name="checkParentType"> </param>
        /// <returns> </returns>
        public static bool IsGenericList(this Type type, bool checkParentType = true)
        {
            if (type == null) return false;
            bool directType = (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (List<>));
            if (!checkParentType || type.BaseType == null || !type.BaseType.IsGenericType) return directType;
            return typeof (List<>).IsSameOrSubclass(type.BaseType.GetGenericTypeDefinition());
        }
        #endregion Class hierarchy, type evaluation ...

        #region Compare
        /// <summary>
        ///   Compare all public fields.
        /// </summary>
        /// <param name="left"> </param>
        /// <param name="right"> </param>
        /// <returns> </returns>
        /// <seealso cref="NullSafeCompareTo(object[])" />
        public static bool MemberwiseCompare(this object left, object right)
        {
            if (left == right) return true;
            if (left == null || right == null) return false;
            if (!left.GetType().IsSameOrSubclass(right.GetType())) return false; // not same class hierarchy
            FieldInfo[] fieldInfos = left.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
            if (fieldInfos.NullSafeCount() < 1) return true; // nothing to compare
            foreach (FieldInfo fi in fieldInfos) {
                object valueLeft = fi.GetValue(left);
                object valueRight = fi.GetValue(right);
                if (valueLeft == valueRight) continue; // null == null, "x" == "x"
                if (valueLeft == null) return false; // right is not null
                if (!valueLeft.Equals(valueRight)) return false;
            }
            // all the same
            return true;
        }

        /// <summary>
        ///   <c>null</c> safe version of compare. Objects must implement <c>IComparable</c>.
        /// </summary>
        /// <param name="o1"> </param>
        /// <param name="o2"> </param>
        /// <returns> </returns>
        /// <seealso cref="IComparable" />
        public static int NullSafeCompareTo(this object o1, object o2)
        {
            if (o1 == o2) return 0;
            IComparable c1 = o1 as IComparable;
            IComparable c2 = o2 as IComparable;
            if (c1 == null) return -1;
            return c2 == null ? 1 : c1.CompareTo(c2);
        }

        /// <summary>
        ///   Null safe <c>Equals</c>.
        /// </summary>
        /// <param name="o1"> </param>
        /// <param name="o2"> </param>
        /// <returns> </returns>
        public static bool NullSafeEquals(this object o1, object o2)
        {
            if (o1 == o2) return true;
            return o1 != null && o1.Equals(o2);
        }

        /// <summary>
        ///   String comparison.
        /// </summary>
        /// <param name="s1"> </param>
        /// <param name="s2"> </param>
        /// <param name="stringComparison"> </param>
        /// <returns> </returns>
        public static bool NullSafeEquals(this string s1, string s2, StringComparison stringComparison)
        {
            if (s1 == s2) return true;
            return s1 != null && s1.Equals(s2, stringComparison);
        }

        /// <summary>
        ///   Case insensitive string comparison.
        /// </summary>
        /// <param name="s1"> </param>
        /// <param name="s2"> </param>
        /// <returns> </returns>
        public static bool NullSafeEqualsIgnoreCase(this string s1, string s2)
        {
            if (s1 == s2) return true;
            return s1 != null && s1.NullSafeEquals(s2, StringComparison.CurrentCultureIgnoreCase);
        }

        /// <summary>
        ///   Compare pairs of objects <c>o[0]</c> vs. <c>o[1]</c>, 
        ///   <c>o[2]</c> vs. <c>o[3]</c>  ...
        /// </summary>
        /// <param name="objectPairs"> Pairwise objects </param>
        /// <returns> </returns>
        /// <seealso cref="MemberwiseCompare" />
        public static int NullSafeCompareTo(params object[] objectPairs)
        {
            if (!objectPairs.NullSafeContainsElements()) return 0;
            for (int i = 0; i < objectPairs.Length; i++) {
                object o1 = objectPairs[i++];
                object o2 = i < objectPairs.Length ? objectPairs[i] : null;
                int r = o1.NullSafeCompareTo(o2);
                if (r != 0) return r;
            }
            return 0;
        }

        /// <summary>
        ///   Is a given object one of any other objects
        /// </summary>
        /// <param name="left"> </param>
        /// <param name="right1"> </param>
        /// <param name="moreRightObjects"> </param>
        /// <returns> </returns>
        public static bool IsAnyObject(this object left, object right1, params object[] moreRightObjects)
        {
            if (left == null) return false;
            if (right1 == left) return true;
            if (moreRightObjects == null || moreRightObjects.Length < 1) return false;
            return moreRightObjects.Any(o => o == left);
        }

        /// <summary>
        ///   Tests whether an object has been disposed.
        /// </summary>
        /// <param name="testCandidate"> </param>
        /// <returns> </returns>
        public static bool IsObjectDisposed(this object testCandidate)
        {
            if (testCandidate == null) return false;
            try {
                // I need to call one method in order to check disposed exception
                // ReSharper disable ReturnValueOfPureMethodIsNotUsed
                testCandidate.ToString();
                // ReSharper restore ReturnValueOfPureMethodIsNotUsed
            }
            catch (ObjectDisposedException) {
                return true;
            }
            return false;
        }

        /// <summary>
        ///   Given <paramref name = "obj" /> is <c>null</c> or <c>default(T)</c>
        ///   for value types.
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "obj"></param>
        /// <returns></returns>
        public static bool IsNullOrDefault<T>(this T obj)
        {
            return Equals(obj, default(T));
        }
        #endregion Compare

        #region Assembly
        /// <summary>
        ///   Fetch some assembly custom attributes.
        /// </summary>
        /// <param name="assembly"> </param>
        /// <param name="title"> </param>
        /// <param name="description"> </param>
        /// <param name="company"> </param>
        public static void GetAssemblyMetadata(this Assembly assembly, out string title, out string description, out string company)
        {
            if (assembly == null) throw new ArgumentNullException("assembly");

            title = null;
            description = null;
            company = null;

            // Iterate through the attributes for the assembly.
            foreach (Attribute attr in Attribute.GetCustomAttributes(assembly)) {
                // ReSharper disable CanBeReplacedWithTryCastAndCheckForNull
                // Check for the AssemblyTitle attribute.
                if (attr is AssemblyTitleAttribute)
                    title = ((AssemblyTitleAttribute) attr).Title;

                    // Check for the AssemblyDescription attribute.
                else if (attr is AssemblyDescriptionAttribute)
                    description = ((AssemblyDescriptionAttribute) attr).Description;

                    // Check for the AssemblyCompany attribute.
                else if (attr is AssemblyCompanyAttribute)
                    description = ((AssemblyCompanyAttribute) attr).Company;
                // ReSharper restore CanBeReplacedWithTryCastAndCheckForNull
            }
        }
        #endregion Assembly

        #region Cloning, casting, new objects
        /// <summary>
        ///   Deep copy of an object, which has to be a <c>SERIALIZABLE</c>.
        ///   This relies on Field serialization, not Property serialization like XML serialization.
        ///   It will be copied via <see cref = "MemoryStream" />.
        ///   <para>
        ///     Via <see cref = "IDeepCopy" /> a post call can used fixing issue because of non serializable field
        ///     (which will be not cloned and need some kind of initialization).
        ///   </para>
        /// </summary>
        /// <param name = "sourceObject"><c>SERIALIZABLE</c> object</param>
        /// <returns>new deeply copied object</returns>
        /// <seealso cref = "MemoryStream" />
        /// <seealso cref = "IDeepCopy" />
        /// <seealso cref = "RecursivelyPostDeepCopy" />
        public static T DeepCopy<T>(this T sourceObject) where T : class
        {
            if (sourceObject.IsNullOrDefault()) return default(T);
            if (!sourceObject.GetType().IsSerializable) throw new SerializationException("Cannot serialize object of type " + sourceObject.GetType().FullName);
            MemoryStream memStream = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(memStream, sourceObject);
            memStream.Position = 0;
            object targetObject = bf.Deserialize(memStream);
            memStream.Close();

            // check
            if (targetObject == null) throw new Exception("Deep copy of " + sourceObject.GetType().FullName + " failed!");

            // if object provides interface call post operation
            targetObject.RecursivelyPostDeepCopy();

            // bye
            return targetObject as T;
        }

        /// <summary>
        ///   Recursively do <see cref = "IDeepCopy.PostDeepCopy" /> operations for public 
        ///   properties of <paramref name = "postDeepCopyObject" />.
        /// </summary>
        /// <param name = "postDeepCopyObject"></param>
        /// <seealso cref = "DeepCopy{T}" />
        /// <seealso cref = "IDeepCopy" />
        private static void RecursivelyPostDeepCopy(this object postDeepCopyObject)
        {
            if (postDeepCopyObject == null) return;
            Type t = postDeepCopyObject.GetType();
            IDeepCopy dc = postDeepCopyObject as IDeepCopy;

            // check for array
            if (t.IsArray) {
                if (t.HasElementType && typeof (IDeepCopy).IsAssignableFrom(t.GetElementType())) {
                    // array of a type implementing IDeepCopy
                    Array iDeepCopyArray = postDeepCopyObject as Array;
                    if (iDeepCopyArray != null) {
                        foreach (var iDeepCopyObject in iDeepCopyArray) {
                            if (iDeepCopyObject != null) iDeepCopyObject.RecursivelyPostDeepCopy();
                        }
                    }
                }
            }
            else if (t.IsGenericList()) {
                var list = (IEnumerable) postDeepCopyObject;
                foreach (var listElement in list) {
                    listElement.RecursivelyPostDeepCopy();
                }
            }
            else if (dc != null) {
                // postDeepCopyObject is IDeepCopy
                PropertyInfo[] propertyInfos = dc.GetType().GetProperties();
                if (propertyInfos.NullSafeContainsElements()) {
                    foreach (PropertyInfo pi in propertyInfos) {
                        try {
                            ParameterInfo[] indexerParams = pi.GetIndexParameters();
                            if (indexerParams.NullSafeCount() > 0) continue; // exclude indexers, indexers "look like a property" but have a parameter, the index (e.g. SerializableDictionary)
                            object pValue = pi.GetValue(postDeepCopyObject, null);
                            if (pValue == postDeepCopyObject) continue; // avoid infinite loops
                            if (pValue != null) pValue.RecursivelyPostDeepCopy();
                        }
                        catch (Exception e) {
                            throw new Exception("Getting value failed", e);
                        }
                    }
                }

                // it is not easy to decide whether this is called before or after the properties of the objects have been
                // handle by PostDeepCopy. It is possible a Property is not yet initialized because PostDeepCopy is not yet called.
                dc.PostDeepCopy();
            }
        }

        /// <summary>
        ///   Deep copy of <paramref name = "sourceObject" />.
        /// </summary>
        /// <typeparam name = "TT"></typeparam>
        /// <typeparam name = "TS"></typeparam>
        /// <param name = "sourceObject"></param>
        /// <returns></returns>
        public static TT DeepCopy<TT, TS>(this TS sourceObject)
            where TS : class
            where TT : class, TS
        {
            object clone = sourceObject.DeepCopy();
            return clone as TT;
        }

        /// <summary>
        ///   Get a new instance by default constructor, constructor with <paramref name = "parameters" />
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "type"></param>
        /// <param name = "parameters"></param>
        /// <returns></returns>
        public static T GetNewObject<T>(this Type type, params object[] parameters)
        {
            if (type == null) throw new ArgumentNullException("type");
            object[] p = parameters ?? new object[0];
            Type[] constructorTypes;
            if (parameters == null || parameters.Length < 1)
                constructorTypes = new Type[0];
            else {
                constructorTypes = new Type[parameters.Length];
                for (int i = 0; i < parameters.Length; i++) {
                    constructorTypes[i] = parameters[i].GetType();
                }
            }
            ConstructorInfo constructor = type.GetConstructor(constructorTypes);

            // ReSharper disable ConditionIsAlwaysTrueOrFalse
            return constructor == null ? default(T) : (T) constructor.Invoke(p);
            // ReSharper restore ConditionIsAlwaysTrueOrFalse
        }

        /// <summary>
        ///   Cast to <c>T</c> if possible, otherwise return <c>Default(T)</c>, which
        ///   is <c>null</c> for most objects.
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "source"></param>
        /// <returns></returns>
        public static T CastIfPossible<T>(this object source)
        {
            if (source == null || !typeof (T).IsSameOrSubclass(source.GetType())) return default(T);
            return (T) source;
        }

        /// <summary>
        ///   <c>null</c> safe cloning of an object
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "t"></param>
        /// <returns></returns>
        public static T NullSafeClone<T>(this T t) where T : class, ICloneable
        {
            if (t == null) return null;
            return (T) t.Clone();
        }
        #endregion Cloning, new objects

        #region Serialization
        /// <summary>
        ///   Serialize an object as Xml string.
        /// </summary>
        /// <param name = "toBeSerialized"></param>
        /// <returns></returns>
        public static string ObjectToXml(this object toBeSerialized)
        {
            string dummy;
            return toBeSerialized.ObjectToXml(out dummy);
        }

        /// <summary>
        ///   Serialize an object as Xml string.
        /// </summary>
        /// <param name = "toBeSerialized"></param>
        /// <param name = "errorMessage"></param>
        /// <returns></returns>
        public static string ObjectToXml(this object toBeSerialized, out string errorMessage)
        {
            errorMessage = null;
            if (toBeSerialized == null) return null;
            string r;
            try {
                XmlSerializer xs = new XmlSerializer(toBeSerialized.GetType());
                using (StringWriter sw = new StringWriter()) {
                    xs.Serialize(sw, toBeSerialized);
                    r = sw.ToString();
                    sw.Close();
                }
            }
            catch (Exception e) {
                r = null;
                errorMessage = e.ExceptionMessageHierarchy(3);
            }
            return r;
        }

        /// <summary>
        ///   Check whether ther are some indication that <paramref name = "toBeSerialized" /> cannot
        ///   be serialized or deep copied.
        /// </summary>
        /// <param name = "toBeSerialized"></param>
        /// <param name = "criticalFields"></param>
        /// <returns><c>true</c> means OK</returns>
        /// <seealso cref = "DeepCopy{T}" />
        /// <seealso cref = "GetSerializableInstanceFieldInfo" />
        public static bool CheckFieldSerialization(this object toBeSerialized, out List<FieldInfo> criticalFields)
        {
            criticalFields = null;
            if (toBeSerialized == null) return true;
            toBeSerialized.GetType().GetSerializableInstanceFieldInfo(out criticalFields);
            return criticalFields.NullSafeIsEmpty();
        }
        #endregion Serialization
    }
}